{{define "component/sortableTableTrigger"}}
    <a-icon type="drag"
        class="sortable-icon"
        style="cursor: move;"
        @mouseup="mouseUpHandler"
        @mousedown="mouseDownHandler"
        @click="clickHandler" />
{{end}}

{{define "component/sortableTable"}}
<script>
    const DRAGGABLE_ROW_CLASS = 'draggable-row';

    const findParentRowElement = (el) => {
        if (!el || !el.tagName) {
            return null;
        } else if (el.classList.contains(DRAGGABLE_ROW_CLASS)) {
            return el;
        } else if (el.parentNode) {
            return findParentRowElement(el.parentNode);
        } else {
            return null;
        }
    }

    Vue.component('a-table-sortable', {
        data() {
            return {
                sortingElementIndex: null,
                newElementIndex: null,
            };
        },
        props: ['data-source', 'customRow'],
        inheritAttrs: false,
        provide() {
            const sortable = {}

            Object.defineProperty(sortable, "setSortableIndex", {
                enumerable: true,
                get: () => this.setCurrentSortableIndex,
            });

            Object.defineProperty(sortable, "resetSortableIndex", {
                enumerable: true,
                get: () => this.resetSortableIndex,
            });

            return {
                sortable,
            }
        },
        render: function (createElement) {
            return createElement('a-table', {
                class: {
                    'ant-table-is-sorting': this.isDragging(),
                },
                props: {
                    ...this.$attrs,
                    'data-source': this.records,
                    customRow: (record, index) => this.customRowRender(record, index),
                },
                on: this.$listeners,
                nativeOn: {
                    drop: (e) => this.dropHandler(e),
                },
                scopedSlots: this.$scopedSlots,
            }, this.$slots.default, )
        },
        created() {
            this.$memoSort = {};
        },
        methods: {
            isDragging() {
                const currentIndex = this.sortingElementIndex;
                return currentIndex !== null && currentIndex !== undefined;
            },
            resetSortableIndex(e, index) {
                this.sortingElementIndex = null;
                this.newElementIndex = null;
                this.$memoSort = {};
            },
            setCurrentSortableIndex(e, index) {
                this.sortingElementIndex = index;
            },
            dragStartHandler(e, index) {
                if (!this.isDragging()) {
                    e.preventDefault();
                    return;
                }
                const hideDragImage = this.$el.cloneNode(true);
                hideDragImage.id = "hideDragImage-hide";
                hideDragImage.style.opacity = 0;
                e.dataTransfer.setDragImage(hideDragImage, 0, 0);
            },
            dragStopHandler(e, index) {
                const hideDragImage = document.getElementById('hideDragImage-hide');
                if (hideDragImage) hideDragImage.remove();
                this.resetSortableIndex(e, index);
            },
            dragOverHandler(e, index) {
                if (!this.isDragging()) {
                    return;
                }

                e.preventDefault();

                const currentIndex = this.sortingElementIndex;
                if (index === currentIndex) {
                    this.newElementIndex = null;
                    return;
                }

                const row = findParentRowElement(e.target);
                if (!row) {
                    return;
                }

                const rect = row.getBoundingClientRect();
                const offsetTop = e.pageY - rect.top;

                if (offsetTop < rect.height / 2) {
                    this.newElementIndex = Math.max(index - 1, 0);
                } else {
                    this.newElementIndex = index;
                }
            },
            dropHandler(e) {
                if (this.isDragging()) {
                    this.$emit('onsort', this.sortingElementIndex, this.newElementIndex);
                }
            },
            customRowRender(record, index) {
                const parentMethodResult = this.customRow?.(record, index) || {};
                const newIndex = this.newElementIndex;
                const currentIndex = this.sortingElementIndex;

                return {
                    ...parentMethodResult,
                    attrs: {
                        ...(parentMethodResult?.attrs || {}),
                        draggable: true,
                    },
                    on: {
                        ...(parentMethodResult?.on || {}),
                        dragstart: (e) => this.dragStartHandler(e, index),
                        dragend: (e) => this.dragStopHandler(e, index),
                        dragover: (e) => this.dragOverHandler(e, index),
                    },
                    class: {
                        ...(parentMethodResult?.class || {}),
                        [DRAGGABLE_ROW_CLASS]: true,
                        ['dragging']: this.isDragging()
                            ? (newIndex === null ? index === currentIndex : index === newIndex)
                            : false,
                    },
                };
            }
        },
        computed: {
            records() {
                const newIndex = this.newElementIndex;
                const currentIndex = this.sortingElementIndex;

                if (!this.isDragging() || newIndex === null || currentIndex === newIndex) {
                    return this.dataSource;
                }

                if (this.$memoSort.newIndex === newIndex) {
                    return this.$memoSort.list;
                }

                let list = [...this.dataSource];
                list.splice(newIndex, 0, list.splice(currentIndex, 1)[0]);

                this.$memoSort = {
                    newIndex,
                    list,
                };

                return list;
            }
        }
    });

    Vue.component('table-sort-trigger', {
        template: `{{template "component/sortableTableTrigger"}}`,
        props: ['item-index'],
        inject: ['sortable'],
        methods: {
            mouseDownHandler(e) {
                if (this.sortable) {
                    this.sortable.setSortableIndex(e, this.itemIndex);
                }
            },
            mouseUpHandler(e) {
                if (this.sortable) {
                    this.sortable.resetSortableIndex(e, this.itemIndex);
                }
            },
            clickHandler(e) {
                e.preventDefault();
            },
        }
    })
</script>

<style>
    @media only screen and (max-width: 767px) {
        .sortable-icon {
            display: none;
        }
    }
    .ant-table-is-sorting .draggable-row td {
        background-color: #ffffff !important;
    }
    .dark .ant-table-is-sorting .draggable-row td {
        background-color: var(--dark-color-surface-100) !important;
    }
    .ant-table-is-sorting .dragging td {
        background-color: rgb(232 244 242) !important;
        color: rgba(0, 0, 0, 0.3);
    }
    .dark .ant-table-is-sorting .dragging td {
        background-color: var(--dark-color-table-hover) !important;
        color: rgba(255, 255, 255, 0.3);
    }
    .ant-table-is-sorting .dragging {
        opacity: 1;
        box-shadow: 1px -2px 2px #008771;
        transition: all 0.2s;
    }
    .ant-table-is-sorting .dragging .ant-table-row-index {
        opacity: 0.3;
    }
</style>
{{end}}
